package com.nowcoder.topic.trie.hard;

/**
 * NC334 字典序第K小
 * @author d3y1
 */
public class NC334 {
    /**
     * 代码中的类名、方法名、参数名已经指定，请勿修改，直接返回方法规定的值即可
     *
     * 类似 -> NC362 字典序排列 [nowcoder]
     *
     * @param n int整型
     * @param k int整型
     * @return int整型
     */
    public int findKth(int n, int k){
        // return solution1(n, k);
        return solution2(n, k);
    }

    private int seq = 0;
    private int ans = 0;

    /**
     * Trie(字典树/前缀树) - 超时!
     * @param n
     * @param k
     * @return
     */
    private int solution1(int n, int k){
        Trie trie = new Trie();
        for(int num=1; num<=n; num++){
            trie.insert(String.valueOf(num));
        }

        preorder(trie, "", k);

        return ans;
    }

    /**
     * 树的前序遍历
     * @param trie
     * @param num
     * @param k
     */
    private void preorder(Trie trie, String num, int k){
        if(seq >= k){
            return;
        }
        if(trie == null){
            return;
        }

        if(trie.isEnd){
            if(++seq == k){
                ans = Integer.parseInt(num);
                return;
            }
        }

        for(int i=0; i<=9; i++){
            preorder(trie.children[i], num+i, k);
        }
    }

    /**
     * Trie类
     */
    private class Trie {
        Trie[] children;
        boolean isEnd;

        public Trie(){
            children = new Trie[10];
            isEnd = false;
        }

        public void insert(String num){
            Trie cur = this;
            int idx;
            for(char digit: num.toCharArray()){
                idx = digit-'0';
                if(cur.children[idx] == null){
                    cur.children[idx] = new Trie();
                }
                cur = cur.children[idx];
            }
            cur.isEnd = true;
        }
    }

    /**
     * 模拟Trie(无需实际构造)
     * @param n
     * @param k
     * @return
     */
    private int solution2(int n, int k){
        // 当前数指针
        int cur = 1;
        k--;

        int cnt;
        while(k > 0){
            // 获取以当前数cur为根的树的所有节点数(包括当前数)
            cnt = cntNodes(cur, n);
            // 不在tree(cur)[以cur为根的树]中
            if(cnt <= k){
                // 当前数指针 右移
                cur++;
                k -= cnt;
            }
            // 必在tree(cur)[以cur为根的树]中
            else{
                // 当前数指针 下移
                cur *= 10;
                k--;
            }
        }

        return cur;
    }

    /**
     * 获取以当前数cur为根的树的所有节点数(包括当前数)
     * @param root
     * @param n
     * @return
     */
    private int cntNodes(int root, int n){
        int cnt = 0;
        long left = root;
        long right = root;

        while(left <= n){
            // 当前层的数目
            cnt += Math.min(right,n)-left+1;
            left = left*10;
            right = right*10+9;
        }

        return cnt;
    }
}